package Simulator;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;

public class GraphicT implements Runnable, ActionListener {
	final String TITLE = "J-MAS : Java Multiprocessor Architecture Simulator ";
	final String VERSION = "v.1.0";
	// --------------Default Operations
	final int PR_RD = 1;
	final int PR_WR = 2;
	final int BUS_RD = 3;
	final int BUS_RD_X = 4;
	final int BUS_UPGR = 5;
	final int FLUSH = 6;
	final int C_C = 7;
	final int M_C = 8;

	JFrame mainFrame;
	JPanel mainPanel;
	String codeFileName;
	Simulator sim;

	JPanel centralP;
	JScrollPane rightScrollP;

	// -------------- Menu Items -------------------
	JMenuItem newF;
	JMenuItem open;
	JMenuItem save;
	JMenuItem saveAs;
	JMenuItem exit;
	JMenuItem help;
	JMenuItem credits;
	JMenuItem speedSettings;
	JMenuItem screenShot;

	// -------------- Settings Items ---------------
	JTextArea instrArea;
	JTextField processedInstr;
	JRadioButton DM, SA;
	JRadioButton MSI, MESI;
	JTextField cacheSize;
	JTextField blockSize;
	JTextField procNum;
	JTextField memSize;
	JTextField cc, mc, bu, fl;
	// -------------- Simulate ---------------------
	JTextField currentOpField;
	JButton simButton;
	// -------------- Memory -----------------------
	List<JTextField> memJTextList;
	JPanel memP;
	// -------------- Processors -------------------
	JPanel stateP;
	// -------------- Settings ---------------------
	JTextField execSp;
	JRadioButton ao;
	JRadioButton sbs;
	JButton confirmSett;

	// ---------------------------------------------
	List<JProcessor> processorList;
	ExecSim thread;
	JButton pauseButton;
	JButton stopButton;
	JBlock lastBlock = null;
	JTextField lastMem = null;
	boolean stopFlag = false;
	int cyclesCounter;
	String cycles;
	Settings globalSettings;

	// ---------------------------------------------

	private JMenuBar createMenu() {
		// Create the menu bar
		JMenuBar menuBar = new JMenuBar();

		// Create a menu
		JMenu file = new JMenu("File");
		menuBar.add(file);

		// Create menu items
		newF = new JMenuItem("New", 'N');
		newF.addActionListener(this);
		newF.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				Event.CTRL_MASK));
		file.add(newF);

		open = new JMenuItem("Open File..", 'O');
		open.addActionListener(this);
		open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
				Event.CTRL_MASK));
		file.add(open);

		file.addSeparator();

		save = new JMenuItem("Save", 'S');
		save.addActionListener(this);
		save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
				Event.CTRL_MASK));
		file.add(save);

		saveAs = new JMenuItem("Save As...", 'A');
		saveAs.addActionListener(this);
		saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
				Event.CTRL_MASK));
		file.add(saveAs);

		file.addSeparator();

		exit = new JMenuItem("Exit");
		exit.addActionListener(this);
		exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
				Event.CTRL_MASK));
		file.add(exit);

		// Create a menu
		JMenu settingsM = new JMenu("Edit");
		menuBar.add(settingsM);

		// Create a menu item
		speedSettings = new JMenuItem("Settings");
		speedSettings.addActionListener(this);
		speedSettings.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
				Event.ALT_MASK));
		settingsM.add(speedSettings);

		// Create a menu item
		screenShot = new JMenuItem("Take a Screenshot");
		screenShot.addActionListener(this);
		screenShot.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
				Event.ALT_MASK));
		settingsM.add(screenShot);

		// Create a menu
		JMenu about = new JMenu("About");
		menuBar.add(about);

		// Create a menu item
		help = new JMenuItem("Help");
		help.addActionListener(this);
		help.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H,
				Event.ALT_MASK));
		about.add(help);

		about.addSeparator();

		// Create a menu item
		credits = new JMenuItem("Credits");
		credits.addActionListener(this);
		about.add(credits);

		return menuBar;
	}

	private JPanel cacheParam() {
		JPanel cacheP = new JPanel(new GridLayout(3, 2));
		cacheP.setBorder(BorderFactory.createTitledBorder("Cache Parameters"));
		ButtonGroup cacheType = new ButtonGroup();
		DM = new JRadioButton("Direct Mapped");
		DM.setSelected(true);
		cacheType.add(DM);
		cacheP.add(DM);
		SA = new JRadioButton("2-Way Associative");
		cacheType.add(SA);
		cacheP.add(SA);

		cacheP.add(new JLabel("Cache Size (Byte):"));
		cacheSize = new JTextField("8");
		cacheP.add(cacheSize);
		cacheP.add(new JLabel("Block Size (Byte):"));
		blockSize = new JTextField("2");
		cacheP.add(blockSize);

		return cacheP;
	}

	private JPanel memAndProc() {
		JPanel cacheP = new JPanel(new GridLayout(2, 2));
		cacheP.setBorder(BorderFactory.createTitledBorder(""));
		cacheP.add(new JLabel("Processors Number:"));
		procNum = new JTextField("4");
		cacheP.add(procNum);
		cacheP.add(new JLabel("Memory Size (Byte):"));
		memSize = new JTextField("64");
		cacheP.add(memSize);
		return cacheP;
	}

	private JPanel protocolType() {
		JPanel cacheP = new JPanel(new GridLayout(1, 2));
		cacheP.setBorder(BorderFactory.createTitledBorder("Protocol Type"));
		ButtonGroup protocolType = new ButtonGroup();

		MSI = new JRadioButton("MSI");
		MSI.addActionListener(this);
		MSI.setSelected(true);
		protocolType.add(MSI);
		cacheP.add(MSI);
		MESI = new JRadioButton("MESI");
		MESI.addActionListener(this);
		protocolType.add(MESI);
		cacheP.add(MESI);
		return cacheP;
	}

	private JPanel cycles() {
		JPanel cyclesP = new JPanel(new GridLayout(4, 2));
		cyclesP.setBorder(BorderFactory.createTitledBorder("Cycles"));
		cyclesP.add(new JLabel("C-C (Cycles):"));
		cc = new JTextField("70");
		cyclesP.add(cc);
		cyclesP.add(new JLabel("C-M (Cycles):"));
		mc = new JTextField("100");
		cyclesP.add(mc);
		cyclesP.add(new JLabel("BusUpgr (Cycles):"));
		bu = new JTextField("15");
		cyclesP.add(bu);
		cyclesP.add(new JLabel("Flush (Cycles):"));
		fl = new JTextField("10");
		cyclesP.add(fl);
		return cyclesP;
	}

	private JPanel instructions() {
		JPanel instructionsP = new JPanel();
		instructionsP.setLayout(new BoxLayout(instructionsP, BoxLayout.Y_AXIS));
		instructionsP.setBorder(BorderFactory
				.createTitledBorder("Instructions List"));

		instrArea = new JTextArea(15, 20);
		instrArea.setEditable(false);
		instrArea.setFont(new Font("SansSerif", Font.PLAIN, 14));
		instrArea
				.setText("To start select a file\nfrom top-left side File Menu.");

		JScrollPane scrollText = new JScrollPane(instrArea);
		scrollText
				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		instructionsP.add(scrollText);
		processedInstr = new JTextField();
		processedInstr.setFont(new Font("SansSerif", Font.BOLD, 14));
		processedInstr.setEditable(false);
		instructionsP.add(processedInstr);
		return instructionsP;
	}

	private JPanel button() {
		JPanel buttonP = new JPanel(new GridLayout(1, 2));
		buttonP.setBorder(BorderFactory.createTitledBorder(""));
		simButton = new JButton("Simulate");
		simButton.addActionListener(this);
		simButton.setEnabled(false);
		buttonP.add(simButton);
		return buttonP;
	}

	private JPanel leftPanel() {
		JPanel settingsP = new JPanel();
		settingsP.setLayout(new BoxLayout(settingsP, BoxLayout.Y_AXIS));
		settingsP.setPreferredSize(new Dimension(300, 400));
		settingsP.setBorder(BorderFactory.createTitledBorder("Settings"));

		settingsP.add(memAndProc());
		settingsP.add(cacheParam());
		settingsP.add(protocolType());
		settingsP.add(instructions());
		settingsP.add(cycles());
		settingsP.add(button());

		return settingsP;
	}

	// ------------- RIGHT PANEL --------------------------------------
	private void setMemory(List<Memory> mList) {
		rightScrollP.setVisible(true);
		JPanel jp = new JPanel(new GridLayout(1, 4));
		jp.add(new JLabel("Address [Tag]", SwingConstants.CENTER));
		jp.add(new JLabel("Data"));
		jp.add(new JLabel("Address [Tag]", SwingConstants.CENTER));
		jp.add(new JLabel("Data"));
		memP.add(jp);

		memJTextList = new LinkedList<JTextField>();
		int i = 0;
		for (Memory m : mList) {
			if (i % 2 == 0)
				jp = new JPanel(new GridLayout(1, 4));
			JLabel lb = new JLabel(m.label + " ["
					+ sim.getTagFromAddress(m.label) + "]",
					SwingConstants.CENTER);
			lb.setAlignmentX(20);
			JTextField tf1 = new JTextField(m.value);
			tf1.setEditable(false);
			memP.add(tf1);
			jp.add(lb);
			jp.add(tf1);
			memP.add(jp);
			memJTextList.add(tf1);
			i++;
		}
		mainFrame.repaint();
		mainFrame.setVisible(true);
	}

	private JScrollPane rightPanel() {
		memP = new JPanel();
		memP.setLayout(new BoxLayout(memP, BoxLayout.Y_AXIS));
		memP.setBorder(BorderFactory.createTitledBorder("Memory"));
		rightScrollP = new JScrollPane(memP,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		rightScrollP.setVisible(false);
		return rightScrollP;
	}

	// ------------------------ CENTRAL -------------------------------------

	private JPanel processorP(final int procId, final int blockNum) {
		JPanel procP = new JPanel(new GridLayout(blockNum + 1, 4));
		procP.setBorder(BorderFactory.createTitledBorder("P" + procId));
		List<JBlock> cellList = new LinkedList<JBlock>();

		procP.add(new JLabel("Block"));
		procP.add(new JLabel("State"));
		procP.add(new JLabel("Tag"));
		procP.add(new JLabel("Data"));
		for (int r = 0; r < blockNum; r++) {
			procP.add(new JLabel("  B" + r));
			JTextField tf1 = new JTextField("I");
			tf1.setEditable(false);
			// tf1.setFont(new Font(, Font., 12));

			procP.add(tf1);
			String zeros = "";
			while (zeros.length() < Integer.parseInt(blockSize.getText()) * 2)
				zeros += "0";
			JTextField tf2 = new JTextField(zeros);
			tf2.setEditable(false);

			procP.add(tf2);
			JTextField tf3 = new JTextField(zeros);
			tf3.setEditable(false);
			procP.add(tf3);
			cellList.add(new JBlock(tf1, tf2, tf3));
		}
		processorList.add(new JProcessor(procId, cellList));
		return procP;
	}

	private JPanel centralPanel() {
		centralP = new JPanel();
		centralP.setVisible(false);
		centralP.setLayout(new BoxLayout(centralP, BoxLayout.Y_AXIS));
		centralP.add(cacheStatesPanel());
		centralP.add(currentOperationPanel());
		return centralP;
	}

	private JScrollPane cacheStatesPanel() {
		stateP = new JPanel();
		stateP.setLayout(new BoxLayout(stateP, BoxLayout.Y_AXIS));
		stateP.setBorder(BorderFactory.createTitledBorder("Cache States"));
		// stateP.setPreferredSize(new Dimension(300, 500));
		JScrollPane scrollProcP = new JScrollPane(stateP,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		return scrollProcP;
	}

	private void setProcessors(final int blockNum, final int procNum) {
		centralP.setVisible(true);
		stateP.removeAll();
		processorList = new LinkedList<JProcessor>();
		for (int i = 0; i < procNum; i++)
			stateP.add(processorP(i, blockNum));
	}

	private JPanel currentOperationPanel() {
		JPanel currentP = new JPanel();
		currentP.setBorder(BorderFactory
				.createTitledBorder("Current Operation"));

		currentOpField = new JTextField("-");
		currentOpField.setPreferredSize(new Dimension(130, 30));
		currentOpField.setEditable(false);
		currentOpField.setFont(new Font("SansSerif", Font.PLAIN, 14));
		currentP.add(currentOpField);
		pauseButton = new JButton("Pause");
		pauseButton.setPreferredSize(new Dimension(100, 30));
		pauseButton.addActionListener(this);
		pauseButton.setEnabled(false);
		currentP.add(pauseButton);

		stopButton = new JButton("Stop");
		stopButton.setPreferredSize(new Dimension(100, 30));
		stopButton.addActionListener(this);
		stopButton.setEnabled(false);
		currentP.add(stopButton);

		return currentP;
	}

	// -----------------------------------------------------------------------------
	public void run() {
		mainFrame = new JFrame(TITLE + VERSION);
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.setJMenuBar(createMenu());

		mainPanel = new JPanel(new GridLayout(1, 3));

		mainPanel.add(leftPanel());
		mainPanel.add(centralPanel());
		mainPanel.add(rightPanel());

		// --------- Initialize Default Settings ----------------
		execSp = new JTextField("500");
		ao = new JRadioButton("All-in-One");
		// All in one execution as default
		sbs = new JRadioButton("Step-by-Step");
		ao.setSelected(true);
		// ------------------------------------------------------

		mainFrame.add(mainPanel);
		mainFrame.pack();
		mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
		mainFrame.setVisible(true);
	}

	public void actionPerformed(ActionEvent e) {
		Object event = e.getSource();

		if (event == newF) {
			instrArea.setText("");
			instrArea.requestFocus();
			codeFileName = null;
			instrArea.setEditable(true);
			simButton.setEnabled(true);
			mainFrame.setTitle(TITLE + VERSION + " - File: New_Untitled_File");
		}
		if (event == open) {
			// Create a file chooser
			final JFileChooser fc = new JFileChooser();
			// In response to a button click:
			int returnVal = fc.showOpenDialog(mainFrame);
			fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				try {
					codeFileName = fc.getSelectedFile().getAbsolutePath();
					instrArea.setText("");
					BufferedReader buff = new BufferedReader(new FileReader(
							codeFileName));
					String line = "";
					while ((line = buff.readLine()) != null) {
						instrArea.append(line + "\n");
					}
					instrArea.setCaretPosition(0);
					//setted editable true for editor
					instrArea.setEditable(true);

					buff.close();
					simButton.setEnabled(true);
					mainFrame.setTitle(TITLE + VERSION + " - File: "
							+ codeFileName);
				} catch (Exception ex) {
					JOptionPane.showMessageDialog(mainFrame,
							"The file is invalid.", "File Error!",
							JOptionPane.ERROR_MESSAGE);
				}

			}
		}
		if (event == saveAs) {
			// Create a file chooser
			final JFileChooser fc = new JFileChooser();
			fc.setDialogType(JFileChooser.SAVE_DIALOG);
			int returnVal = fc.showSaveDialog(mainFrame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				try {
					codeFileName = fc.getSelectedFile().getAbsolutePath();
					BufferedWriter buff = new BufferedWriter(new FileWriter(fc
							.getSelectedFile()));
					buff.write(instrArea.getText());
					buff.close();
					JOptionPane.showMessageDialog(mainFrame, "File Saved.",
							"Done", JOptionPane.INFORMATION_MESSAGE);
					mainFrame.setTitle(TITLE + VERSION + " - File: "
							+ codeFileName);
				} catch (Exception ex) {
					JOptionPane.showMessageDialog(mainFrame,
							"Could not save the file.", "File Error!",
							JOptionPane.ERROR_MESSAGE);
				}
			}
		}
		if (event == save) {
			try {
				if (codeFileName == null)
					saveAs.doClick();
				else {
					BufferedWriter buff = new BufferedWriter(new FileWriter(
							codeFileName));
					buff.write(instrArea.getText());
					buff.close();
					JOptionPane.showMessageDialog(mainFrame, "File Saved.",
							"Done", JOptionPane.INFORMATION_MESSAGE);
				}
			} catch (Exception ex) {
				JOptionPane.showMessageDialog(mainFrame,
						"Could not save the file.", "File Error!",
						JOptionPane.ERROR_MESSAGE);
			}
		}
		if (event == credits) {
			JOptionPane.showMessageDialog(mainFrame,
					"SCIUTO Lorenzo - sciuto.lorenzo@gmail.com\n"
							+ "URZI' Erik - erik.urzi@gmail.com\n"
							+ "COPPOLA Giuseppe - pinuztribe@gmail.com",

					"J-MAS: Authors", JOptionPane.INFORMATION_MESSAGE);
		}
		if (event == exit) {
			int answer = JOptionPane.showConfirmDialog(mainFrame,
					"Do you really want to close?", "Close",
					JOptionPane.YES_NO_OPTION);
			if (answer == JOptionPane.YES_OPTION)
				System.exit(0);
		}
		if (event == simButton) {

			cc.setEditable(false);
			mc.setEditable(false);
			fl.setEditable(false);
			bu.setEditable(false);

			instrArea.setEditable(false);
			simButton.setEnabled(false);

			stopButton.setEnabled(true);
			pauseButton.setEnabled(true);

			speedSettings.setEnabled(false);

			newF.setEnabled(false);
			save.setEnabled(false);
			saveAs.setEnabled(false);
			open.setEnabled(false);

			thread = new ExecSim();
			thread.start();

		}
		if (event == pauseButton) {
			// Pause the thread
			synchronized (thread) {
				if (thread.pleaseWait == true) {
					thread.pleaseWait = false;
					pauseButton.setText("Pause");
					stopButton.setEnabled(true);
					speedSettings.setEnabled(false);
					thread.notify();
				} else {
					thread.pleaseWait = true;
					pauseButton.setText("Play");
					stopButton.setEnabled(false);
					speedSettings.setEnabled(true);
				}
			}
		}
		if (event == stopButton) {
			stopFlag = true;
		}
		if (event == speedSettings) {
			JFrame speedFrame = new JFrame("J-MAS : Settings");
			speedFrame.setResizable(false);
			JPanel speedPanel = new JPanel(new GridLayout(3, 2));
			speedPanel.setBorder(BorderFactory
					.createTitledBorder("Execution Mode"));
			ButtonGroup execTypeG = new ButtonGroup();

			speedPanel.add(ao);
			execTypeG.add(ao);
			ao.setFont(new Font("SansSerif", Font.BOLD, 14));

			speedPanel.add(new JLabel(""));

			execTypeG.add(sbs);
			speedPanel.add(sbs);
			sbs.setFont(new Font("SansSerif", Font.BOLD, 14));

			speedPanel.add(new JLabel(""));
			JLabel jl = new JLabel("Exec Speed (Millis):",
					SwingConstants.CENTER);
			jl.setFont(new Font("SansSerif", Font.BOLD, 14));
			speedPanel.add(jl);

			speedPanel.add(execSp);
			execSp.setFont(new Font("SansSerif", Font.PLAIN, 14));

			speedFrame.add(speedPanel);
			speedFrame.pack();
			speedFrame.setVisible(true);
		}
		if (event == screenShot) {
			// Create a file chooser
			final JFileChooser fc = new JFileChooser();
			fc.setDialogType(JFileChooser.SAVE_DIALOG);
			int returnVal = fc.showSaveDialog(mainFrame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				try {
					Toolkit toolkit = Toolkit.getDefaultToolkit();
					Dimension screenSize = toolkit.getScreenSize();
					Rectangle screenRect = new Rectangle(screenSize);
					Robot robot = new Robot();
					BufferedImage image = robot.createScreenCapture(screenRect);
					ImageIO.write(image, "jpg", new File(fc.getSelectedFile()
							.getAbsolutePath()));
				} catch (Exception ex) {
					JOptionPane.showMessageDialog(mainFrame,
							"Could not save the image.", "File Error!",
							JOptionPane.ERROR_MESSAGE);
				}
			}

		}
		if (event == help) {
			try {
				BufferedReader ita = new BufferedReader(new FileReader(
						"text/ita.txt"));
				BufferedReader eng = new BufferedReader(new FileReader(
						"text/eng.txt"));
				String line = "";

				JFrame helpFrame = new JFrame("J-MAS : Help");
				JTabbedPane tPane = new JTabbedPane();
				tPane.setPreferredSize(new Dimension(600, 500));

				// ------------ Italian Help ---------------------
				JPanel itaPanel = new JPanel();
				itaPanel.setBackground(Color.white);

				itaPanel.setBorder(BorderFactory
						.createTitledBorder("Help - Italiano"));

				JTextArea itaT = new JTextArea();

				itaT.setEditable(false);

				JScrollPane scrollIta = new JScrollPane(itaT);
				scrollIta
						.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

				while (((line = ita.readLine()) != null)) {
					itaT.append(line + "\n");
				}
				itaT.setCaretPosition(0);
				itaPanel.add(scrollIta);
				// ------------ English Help ---------------------
				JPanel engPanel = new JPanel();
				engPanel.setBackground(Color.white);

				engPanel.setBorder(BorderFactory
						.createTitledBorder("Help - English"));

				JTextArea engT = new JTextArea();

				JScrollPane scrollEng = new JScrollPane(engT);
				scrollEng
						.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

				engT.setEditable(false);

				while (((line = eng.readLine()) != null)) {
					engT.append(line + "\n");
				}
				engT.setCaretPosition(0);
				engPanel.add(scrollEng);
				// -----------------------------------------------

				tPane.addTab("ITALIANO", scrollIta);
				tPane.addTab("ENGLISH", scrollEng);

				helpFrame.add(tPane);
				helpFrame.pack();
				helpFrame.setVisible(true);
				ita.close();
				eng.close();
			} catch (FileNotFoundException e1) {
				JOptionPane.showMessageDialog(mainFrame,
						"Could not find the help txt file.", "File Error!",
						JOptionPane.ERROR_MESSAGE);
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}

	}

	private class ExecSim extends Thread {
		boolean pleaseWait = false;

		// ------------------ EXECUTION FUNCTIONS -------------------------
		private void refreshMemory(Memory memBlock) {
			Color defaultColor = currentOpField.getBackground();

			if (lastMem != null) {
				lastMem.setBackground(defaultColor);
			}
			lastMem = memJTextList.get(memBlock.id);
			lastMem.setText(memBlock.value);
			lastMem.setBackground(Color.green);
		}

		private void refreshCache(Block myBlock, int procId) {
			Color defaultColor = currentOpField.getBackground();
			String myState = (myBlock.state == 1 ? "M"
					: (myBlock.state == 2 ? "S" : (myBlock.state == 3 ? "I"
							: "E")));
			if (lastMem != null) {
				lastMem.setBackground(defaultColor);
			}
			if (lastBlock != null) {
				lastBlock.data.setBackground(defaultColor);
				lastBlock.tag.setBackground(defaultColor);
				lastBlock.state.setBackground(defaultColor);
			}
			
			lastBlock = processorList.get(procId).blockList.get(myBlock.id);
			lastBlock.data.setText(myBlock.data);
			lastBlock.data.setBackground(Color.green);

			lastBlock.tag.setText(myBlock.tag);
			lastBlock.tag.setBackground(Color.green);

			lastBlock.state.setText(myState);
			lastBlock.state.setBackground(Color.green);
		}

		private void processInstruction(final String line) {
			List<Instruction> instructionList = sim.start(line);
			for (Instruction myInstr : instructionList) {
				if (stopFlag == true)
					return;
				synchronized (this) {
					while (pleaseWait) {
						try {
							wait();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				processedInstr.setText("Processing: " + line);

				if (myInstr.block instanceof Block) {
					refreshCache((Block) myInstr.block, myInstr.processorId);
				} else if (myInstr.block instanceof Memory) {
					if (myInstr.action == 8) {
						Memory myMem = (Memory) myInstr.block;
						Block myBlock = new Block(sim.getBlock(myMem.label,
								myInstr.processorId));
						myBlock.data = myMem.value;
						refreshCache(myBlock, myInstr.processorId);
					}
					refreshMemory((Memory) myInstr.block);
				}

				/*
				 * PR_RD = 1; PR_WR = 2; BUS_RD = 3; BUS_RD_X = 4; BUS_UPGR = 5;
				 * FLUSH = 6; C_C = 7; M_C = 8 ;
				 */

				String[] operationArray = { "Change Block State", "PrRd",
						"PrWr", "BusRd", "BusRdx", "BusUpgr", "Flush",
						"C-C Read", "M-C Read" };
				currentOpField.setText(operationArray[myInstr.action]
						+ (myInstr.action == FLUSH ? " (P"
								+ myInstr.processorId + ")" : ""));
				mainFrame.repaint();
				mainFrame.setVisible(true);
				if (ao.isSelected()) {
					// Exec all in one.
					try {
						Thread.sleep(Integer.parseInt(execSp.getText()));
					} catch (InterruptedException e) {
						JOptionPane
								.showMessageDialog(
										mainFrame,
										"An error occurred during simulation.\nPlease report the bug.",
										"ERROR-1", JOptionPane.ERROR_MESSAGE);
						e.printStackTrace();
					}
				} else {
					// Exec step by step.
					pauseButton.doClick();
				}

				if (myInstr.action == C_C) {
					cyclesCounter += Integer.parseInt(cc.getText());
					cycles += operationArray[myInstr.action] + " (P"
							+ myInstr.processorId + ") : " + cc.getText()
							+ " cycles\n";
				} else if (myInstr.action == M_C) {
					cyclesCounter += Integer.parseInt(mc.getText());
					cycles += operationArray[myInstr.action] + " (P"
							+ myInstr.processorId + ") : " + mc.getText()
							+ " cycles\n";
				} else if (myInstr.action == FLUSH) {
					cyclesCounter += Integer.parseInt(fl.getText());
					cycles += operationArray[myInstr.action] + " (P"
							+ myInstr.processorId + ") : " + fl.getText()
							+ " cycles\n";
				} else if (myInstr.action == BUS_UPGR) {
					cyclesCounter += Integer.parseInt(bu.getText());
					cycles += operationArray[myInstr.action] + " (P"
							+ myInstr.processorId + ") : " + bu.getText()
							+ " cycles\n";
				}
			}
		}

		// Execution Thread. Exec the real simulation.
		public void run() {
			try {
				setProcessors(Integer.parseInt(cacheSize.getText())
						/ Integer.parseInt(blockSize.getText()), Integer
						.parseInt(procNum.getText()));
				globalSettings = new Settings((MSI.isSelected() ? 0 : 1), (DM
						.isSelected() ? 0 : 1), Integer.parseInt(cacheSize
						.getText()), Integer.parseInt(blockSize.getText()),
						Integer.parseInt(procNum.getText()), Integer
								.parseInt(memSize.getText()), Integer
								.parseInt(cc.getText()), Integer.parseInt(mc
								.getText()), Integer.parseInt(bu.getText()),
						Integer.parseInt(fl.getText()));
				sim = new Simulator(globalSettings);
				memP.removeAll();
				setMemory(sim.setMemory());

				int answer = JOptionPane.showConfirmDialog(mainFrame,
						"Start Simulation?", "Simulation",
						JOptionPane.YES_NO_OPTION);
				if (answer == JOptionPane.YES_OPTION) {
					cyclesCounter = 0;
					cycles = "";

					String[] lines = instrArea.getText().split("\\n");
					for (String line : lines) {
						processInstruction(line);
					}
					// ----------------- Cycles Popup -------------------------
					JFrame cyclesF = new JFrame("Done!");
					cyclesF.setLocation(400, 200);
					// cyclesF.setBackground(Color.white);
					JPanel cyclesP = new JPanel();
					cyclesP.setLayout(new BoxLayout(cyclesP, BoxLayout.Y_AXIS));
					// cyclesP.setBackground(Color.white);
					JTextArea cyclesT = new JTextArea();
					cyclesT.setFont(new Font("SansSerif", Font.BOLD, 12));
					JScrollPane cyclesScroll = new JScrollPane(cyclesT);
					cyclesT.setBackground(cyclesP.getBackground());
					cyclesT.setText(cycles);
					cyclesT.setPreferredSize(new Dimension(100, 300));
					cyclesScroll
							.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
					JLabel jl = new JLabel("TOTAL CYCLES: " + cyclesCounter);
					cyclesT.setEditable(false);
					cyclesT.setCaretPosition(0);
					cyclesP.add(cyclesScroll);
					cyclesP.add(jl);
					cyclesF.add(cyclesP);
					cyclesF.pack();
					cyclesF.setVisible(true);
					cyclesF.requestFocus();
					// JOptionPane.showMessageDialog(mainFrame,
					// ,JOptionPane.INFORMATION_MESSAGE);
					// --------------------------------------------------------
				}
			} catch (NumberFormatException ex) {
				JOptionPane
						.showMessageDialog(
								mainFrame,
								"The input file is not well formed.\nStandard Format:\nProcessor,Operation,Block,Data\n[No spaces between operand are allowed, just commas]",
								"ERROR-4", JOptionPane.ERROR_MESSAGE);
				ex.printStackTrace();

			} catch (Exception ex) {
				JOptionPane
						.showMessageDialog(
								mainFrame,
								"Invalid Options.\nCheck and Correct mistakes from settings panel",
								"ERROR-5", JOptionPane.ERROR_MESSAGE);
				ex.printStackTrace();
			}

			cc.setEditable(true);
			mc.setEditable(true);
			fl.setEditable(true);
			bu.setEditable(true);

			instrArea.setEditable(true);
			simButton.setEnabled(true);

			stopButton.setEnabled(false);
			pauseButton.setEnabled(false);
			stopFlag = false;

			speedSettings.setEnabled(true);

			newF.setEnabled(true);
			save.setEnabled(true);
			saveAs.setEnabled(true);
			open.setEnabled(true);

			// mainFrame.setVisible(true);
			mainFrame.repaint();
		}
	}
}